
#include "CdnNode.h"
#include "FileManager.h"


FileManager::FileManager(): curr_node_id(0)
{
    //config cdn cache nodes info
    ifstream ifile("config/nodes.config");
    if (!ifile) {
        logger.log("VSS node config file error", LOG_LEVEL_FATAL);
        exit(1);
    }
    
    string node_line; 
    while (getline(ifile, node_line)) {
        CdnNodeRecord *node_rec = new CdnNodeRecord;
        node_rec->transfer_port = node_line;
        server_nodes.push_back(node_rec);
    }

    //config cdn file list info
    TiXmlDocument* filelist_xml_doc = new TiXmlDocument();
    filelist_xml_doc->LoadFile("config/filelist.xml");
    TiXmlElement* filelist_xml = filelist_xml_doc->RootElement();
    TiXmlElement* stream_obj = filelist_xml->FirstChildElement();

    while (stream_obj) {
        CdnFileRecord* cf = new CdnFileRecord();
        cf->cdn_file_rate = CDN_STREAMING_RATE_STD_MBPS; 
        TiXmlElement* file_attr = stream_obj->FirstChildElement();

        while (file_attr)
        {
            string attr_val = file_attr->Value();

            if (attr_val == "name") {
                cf->cdn_file_subname = file_attr->GetText(); 
            } else if (attr_val == "path") {
                cf->cdn_file_transfer_path = file_attr->GetText(); 
            } else if (attr_val == "size") {
                string len_str = file_attr->GetText();
                cf->cdn_file_len = strtoll(len_str.c_str(), NULL, 10);
            } else if (attr_val == "transferid") {
                cf->cdn_file_transfer_id = file_attr->GetText(); 
            } else if (attr_val == "rate") {
                string rate_str = file_attr->GetText();
                cf->cdn_file_rate = strtol(rate_str.c_str(), NULL, 10);
            }

            file_attr = file_attr->NextSiblingElement();
        }
        
        if (cf->cdn_file_len <= 0) {
            cout << "Config File Error: unexpected file len" << endl;
            delete cf;
        } else {
            file_list.insert(CdnFileListRecord(cf->cdn_file_subname, cf));
            file_list_index.insert(CdnFileListRecord(cf->cdn_file_transfer_id, cf));
        }

        stream_obj = stream_obj->NextSiblingElement();
    }

    delete filelist_xml_doc;

#ifdef FAKE_DATA
    fake_file = new CdnFileRecord();
    fake_file->cdn_file_len = CDN_FILE_FAKE_LEN;
    fake_file->cdn_file_rate = CDN_STREAMING_RATE_STD_MBPS; 
    fake_stream = new CdnDiskStream(0, fake_file, 0);
#endif
}


FileManager::~FileManager()
{
    vector<CdnNodeRecord*>::iterator it = server_nodes.begin();
    while (it != server_nodes.end()) {
        delete *it;
        ++it;
    }

    map<string, CdnFileRecord*>::iterator ir = file_list.begin();
    while (ir != file_list.end()) {
        delete ir->second;
        ++ir;
    }

#ifdef FAKE_DATA
    delete fake_file;
    delete fake_stream;
#endif

}

void FileManager::GetFileLocation(string &filename, string &transfer_port)
{
    if (server_nodes.empty()) {
        transfer_port = "";
        return;
    }

    transfer_port = server_nodes[curr_node_id]->transfer_port;    
    curr_node_id++;

    if (curr_node_id >= server_nodes.size()) {
        curr_node_id = 0;
    }
}

CdnFileRecord *FileManager::GetFileObjWithName(const string &filename)
{
    map<string, CdnFileRecord*>::iterator ir = file_list.find(filename);
    if (ir != file_list.end()) {
        return ir->second;
    }

#ifdef FAKE_DATA
    return fake_file;
#endif
    return 0;
}

CdnFileRecord *FileManager::GetFileObjWithId(const string &transfer_id)
{
    map<string, CdnFileRecord*>::iterator ir = file_list_index.find(transfer_id);
    if (ir != file_list_index.end()) {
        return ir->second;
    }

#ifdef FAKE_DATA
    return fake_file;
#endif
    return 0;
}

CdnDiskStream *FileManager::OpenDiskStream(CdnFileRecord *cdn_file, fw_file_size_t pos)
{
#ifdef FAKE_DATA
    if (cdn_file == fake_file) return fake_stream;
#endif

    ifstream *inp = new ifstream(cdn_file->cdn_file_transfer_path.c_str());

    if (*inp) {
        if (pos > 0) {
            (*inp).seekg(pos);
        }
    } else { //Unable to open file
        delete inp;
        return 0;
    }

    return new CdnDiskStream(inp, cdn_file, pos);
}

void FileManager::CloseDiskStream(CdnDiskStream *disk_stream)
{
#ifdef FAKE_DATA
    if (disk_stream == fake_stream) return;
#endif

    delete disk_stream;
    return;
}

bool CdnDiskStream::ReadFileBlock(char *buf, size_t rlen)
{
#ifdef FAKE_DATA
    if (file_manager.isFakeStream(this)) return true;
#endif
    
    if (!this->file_stream_sys || !(*(this->file_stream_sys))) { //ifstream error
        return false;
    }

    (*(this->file_stream_sys)).read(buf, rlen);
    return true;
}

bool CdnDiskStream::Seek(fw_file_size_t abs_pos) 
{
    this->pos = abs_pos;
#ifdef FAKE_DATA
    if (file_manager.isFakeStream(this)) return true;
#endif
    
    if (!file_stream_sys || !(*file_stream_sys)) { //ifstream error
        return false;
    }

    (*file_stream_sys).seekg(abs_pos);
    return true;
}




